package com.ruoyi.web.core.config;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import org.flywaydb.core.Flyway;
import org.springframework.boot.context.event.ApplicationContextInitializedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.Ordered;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.stereotype.Component;

/**
 * @author zhousiren
 * @since 2025/1/6 12:00
 */
@Component
public class FlywayInitListener implements Ordered, ApplicationListener<ApplicationContextInitializedEvent> {
    private static final String FLYWAY_LOCATIONS = "classpath:db/migration";
    @Override
    public void onApplicationEvent(ApplicationContextInitializedEvent event) {
        ConfigurableEnvironment environment = event.getApplicationContext().getEnvironment();

        // 获取数据库连接配置
        String driverClassName = environment.getProperty("spring.datasource.driver-class-name");
        String dataSourceUrl = environment.getProperty("spring.datasource.druid.master.url");
        String dataSourceUsername = environment.getProperty("spring.datasource.druid.master.username");
        String dataSourcePassword = environment.getProperty("spring.datasource.druid.master.password");
        // flyway的配置
        String enabledStr = environment.getProperty("spring.flyway.enabled");
        String locations = environment.getProperty("spring.flyway.locations");
        String baselineOnMigrateStr = environment.getProperty("spring.flyway.baseline-on-migrate");
        String outOfOrderStr = environment.getProperty("spring.flyway.out-of-order");
        String placeholder = environment.getProperty("spring.flyway.placeholder-replacement");

        // 是否开启flyway，默认false.
        boolean enabled = false;
        if (StrUtil.isNotBlank(enabledStr)) {
            enabled = Boolean.parseBoolean(enabledStr);
        }
        // 如果未开启flyway 直接return
        if (!enabled) {
            return;
        }
        // 如果有为空的配置，终止执行
        if (ObjectUtil.hasEmpty(dataSourceUrl, dataSourceUsername, dataSourcePassword, driverClassName)) {
            throw new RuntimeException("获取不到application.yml中的数据库配置，无法为flyway创建数据库链接，请检查spring.datasource配置");
        }
        // 当迁移时发现目标schema非空，而且带有没有元数据的表时，是否自动执行基准迁移，默认false.
        boolean baselineOnMigrate = false;
        if (StrUtil.isNotBlank(baselineOnMigrateStr)) {
            baselineOnMigrate = Boolean.parseBoolean(baselineOnMigrateStr);
        }

        // 如果未设置flyway路径，则设置为默认flyway路径
        if (StrUtil.isBlank(locations)) {
            locations = FLYWAY_LOCATIONS;
        }

        // 是否允许无序的迁移 开发环境最好开启, 生产环境关闭
        boolean outOfOrder = false;
        if (StrUtil.isNotBlank(outOfOrderStr)) {
            outOfOrder = Boolean.parseBoolean(outOfOrderStr);
        }

        // 是否开启占位符
        boolean enablePlaceholder = true;
        if (StrUtil.isNotBlank(placeholder)) {
            enablePlaceholder = Boolean.parseBoolean(placeholder);
        }

        DriverManagerDataSource dmDataSource = null;
        try {
            assert dataSourceUrl != null;
            // 手动创建数据源
            dmDataSource = new DriverManagerDataSource();
            dmDataSource.setDriverClassName(driverClassName);
            dmDataSource.setUrl(dataSourceUrl);
            dmDataSource.setUsername(dataSourceUsername);
            dmDataSource.setPassword(dataSourcePassword);

            // flyway配置
            Flyway flyway = Flyway.configure()
                    .dataSource(dmDataSource)

                    // 迁移脚本的位置
                    .locations(locations)

                    // 当迁移时发现目标schema非空，而且带有没有元数据的表时，是否自动执行基准迁移
                    .baselineOnMigrate(baselineOnMigrate)

                    // 是否允许无序的迁移 开发环境最好开启 , 生产环境关闭
                    .outOfOrder(outOfOrder)

                    // 是否开启占位符
                    .placeholderReplacement(enablePlaceholder)

                    // 忽略丢失的脚本
                    .ignoreMissingMigrations(true)

                    .load();

            // 执行迁移
            flyway.migrate();

        } catch (Exception e) {
            throw new RuntimeException(StrUtil.format("脚本错误，flyway执行迁移异常，具体原因：{}",e.getMessage()));
        }

    }

    @Override
    public int getOrder() {
         return Ordered.HIGHEST_PRECEDENCE;
    }
}
